package com.kvn.backdoor.base;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.kvn.backdoor.common.BackdoorErrorCode;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.StringUtils;

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by wangzhiyuan on 2018/8/16
 */
public abstract class XmlManager {
    protected abstract String getFile();

    private String getFileAbsoutePath(){
        return System.getProperty("user.dir") + "\\" + getFile();
    }

    protected synchronized void saveContent(Object content) {
        Element root = getRootElement();

        List elements = root.elements();
        Integer maxId;
        if (elements.size() <= 0) {
            maxId = 0;
        } else {
            String text = ((Element) elements.get(elements.size() - 1)).getText();
            maxId = Integer.valueOf(JSONObject.parseObject(text).get("id").toString());
        }
        int nextId = maxId + 1;
        // 设置id属性的值
        new BeanWrapperImpl(content).setPropertyValue("id", nextId);

        // 操作xml
        root.addElement("data").addAttribute("id", String.valueOf(nextId)).addText(JSON.toJSONString(content));
        // 写文件
        write2file(root.getDocument());

    }

    protected <T> List<T> getAllData(Class<T> dataType){
        Element root = getRootElement();
        List elements = root.elements();
        List<T> rltList = new ArrayList<>();
        elements.forEach(item -> {
            Element element = (Element) item;
            String text = element.getText();
            T obj = JSONObject.parseObject(text, dataType);
            rltList.add(obj);
        });
        return rltList;
    }

    protected List<String> getAllRawData(){
        Element root = getRootElement();
        List elements = root.elements();
        List rltList = new ArrayList();
        elements.forEach(item -> {
            Element element = (Element) item;
            String text = element.getText();
            rltList.add(text);
        });
        return rltList;
    }

    private void write2file(Document document) {
        XMLWriter writer = null;
        try (FileOutputStream os = new FileOutputStream(getFileAbsoutePath())){
            // 排版缩进的格式
            OutputFormat format = OutputFormat.createPrettyPrint();
            writer = new XMLWriter(new OutputStreamWriter(os, "UTF-8"), format);
            // 写入
            writer.write(document);
            // 立即写入
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private Element getRootElement() {
        ensureExist(getFile());
        try {
            // 创建saxReader对象
            SAXReader reader = new SAXReader();
            // 通过read方法读取一个文件 转换成Document对象
            Document document = reader.read(getFileAbsoutePath());
            //获取根节点元素对象
            Element root = document.getRootElement();

            return root == null ? document.addElement(getFile().substring(0, getFile().indexOf("."))) : root;
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }

    private void ensureExist(String file) {
        Path path = Paths.get(file);

        try {
            if (Files.exists(path)) {
                if (StringUtils.hasText(new String(Files.readAllBytes(path)))) {
                    // 文件存在且不为空，则返回
                    return;
                } else {
                    // 删除空文件
                    Files.delete(path);
                }
            }

            Files.createFile(path);
        } catch (IOException e) {
            throw BackdoorErrorCode.CREATE_FILE_ERROR.exp(e, file);
        }

        // 初始化文件
        path = Paths.get(file);
        String rootElementName = getFile().substring(0, getFile().lastIndexOf("."));
        String initContent = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<" + rootElementName + "> \n" +
                "</" + rootElementName + ">";
        try(BufferedWriter bfw = Files.newBufferedWriter(path)) {
            bfw.write(initContent);
            bfw.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据 id 来更新 xml 中的数据。全量更新（直接根据 id 覆盖）
     * @param entity 待更新的实体
     */
    public void updateById(Object entity) {
        BeanWrapper beanWrapper = new BeanWrapperImpl(entity);
        Object id = beanWrapper.getPropertyValue("id");
        if (id == null) {
            throw BackdoorErrorCode.ID_ISNULL.exp();
        }
        Element root = getRootElement();
        List elements = root.elements();
        boolean flag = false; // 是否更新到
        for (Object item : elements) {
            Element element = (Element) item;
            if(idMatches(id.toString(), element)){
                element.setText(JSON.toJSONString(entity));
                write2file(element.getDocument());
                flag = true;
                break;
            }
        }

        if (!flag) {
            throw BackdoorErrorCode.DATA_NOT_EXIST.exp(id);
        }
    }

    /**
     * 判断 element 中的 id 是否与传入的 id 匹配
     * @param id
     * @param element
     * @return
     */
    private boolean idMatches(String id, Element element) {
        Object idInXml = null;
        Attribute idAttr = element.attribute("id");
        if (idAttr != null) {
            idInXml = idAttr.getValue();
        } else {
            idInXml = JSONObject.parseObject(element.getText()).get("id");
        }


        return idInXml != null && id.equals(idInXml.toString());
    }

    /**
     * 根据 id 来删除元素
     * @param id
     */
    public void deleteById(String id) {
        Element root = getRootElement();
        List elements = root.elements();
        boolean flag = false; // 是否更新到
        for (Object item : elements) {
            Element element = (Element) item;
            if(idMatches(id, element)){
                root.remove(element); // 删除
                write2file(root.getDocument());
                flag = true;
                break;
            }
        }

        if (!flag) {
            throw BackdoorErrorCode.DATA_NOT_EXIST.exp(id);
        }
    }
}
